#define TAG                         "ThreadPool"

#include "ThreadPool.h"

#define TASK_MAX_THRESHHOLD         6 // 这里调成4会发生队列任务满了的情况,应该是最大任务数超过最大线程数2倍比较有可能出现任务队列满的情况
#define THREAED_CORE_THRESHHOLD     2 // 核心线程数
#define THREAED_MAX_THRESHHOLD      4  // 最大线程数
#define THREAD_MAX_IDLE_TIME        5    // 单位：秒

enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };

#define log_trace(...) logLog(LOG_TRACE, __VA_ARGS__)
#define log_debug(...) logLog(LOG_DEBUG, __VA_ARGS__)
#define log_info(...)  logLog(LOG_INFO,  __VA_ARGS__)
#define log_warn(...)  logLog(LOG_WARN,  __VA_ARGS__)
#define log_error(...) logLog(LOG_ERROR, __VA_ARGS__)
#define log_fatal(...) logLog(LOG_FATAL, __VA_ARGS__)

// 必须在类外初始化
ThreadPool *ThreadPool::singleton = nullptr;
int Thread::_generateId = 0;
LogLog logLog; // 存储回调函数的指针

// 创建一个线程函数，这里的thread的方法就是在构造函数里面执行的
void Thread::start() {
    std::thread t(_func, _threadId); // C++11 来说，线程对象t 和线程函数func_
    t.detach(); // 设置为分离线程，主线程和从线程func_
}

int Thread::getId() const {
    return _threadId;
}

void ThreadPool::exit() {
    _isPoolRunning = false;

    // 等待线程池里面所有的线程返回，有两种状态： 阻塞 & 正在执行的任务
    std::unique_lock<std::mutex> lock{_taskQueMtx};
    _nonEmpty.notify_all(); // 唤醒所有的线程
    // 所有线程都释放，这里才会彻底退出
    _exitCond.wait(lock, [&]() -> bool { return _threads.size() == 0; });
}

ThreadPool::ThreadPool() {
    _isPoolRunning = false;

    _coreThreadSize = THREAED_CORE_THRESHHOLD;
    _curThreadSize = 0;
    _threadMaxThreshHold = THREAED_MAX_THRESHHOLD;
    _taskSize = 0;
    _taskQueMaxThreshHold = TASK_MAX_THRESHHOLD;
    _poolMode = ThreadPoolMode::ThreadPoolModeCACHED;
    _idleThreadSize = 0;
}

ThreadPool::~ThreadPool() {
    exit();
}

ThreadPool *ThreadPool::getInstance() {
    if (ThreadPool::singleton == nullptr) {
        singleton = new ThreadPool();
    }
    return singleton;
}

void ThreadPool::setLog(LogLog log) {
    logLog = log;
}

void ThreadPool::start() {
    // 设置线程池的运行状态
    _isPoolRunning = true;
    // 记录初始线程的个数
    _curThreadSize = _coreThreadSize;

    // 创建线程对象
    for (int i = 0; i < _coreThreadSize; ++i) {
        // 这里把线程函数传递给线程
        auto ptr = std::make_unique<Thread>(
                std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
        int threadId = ptr->getId();
        _threads.emplace(threadId, std::move(ptr));
        /*threads_.emplace_back(std::move(ptr));*/
    }

    // 启动所有线程
    for (int i = 0; i < _coreThreadSize; ++i) {
        // 创建线程，启动线程，执行线程函数：即查看任务队列是否有任务，若有就拿走执行
        _threads[i]->start();
        _idleThreadSize++; // 记录初始空闲线程的数量
    }
    log_debug("%s start %d threads in threadPool",TAG,_coreThreadSize);
}

void ThreadPool::threadFunc(int threadId) {
    // 获取一个高精度时间
    auto lastTime = std::chrono::high_resolution_clock().now();
    while (_isPoolRunning) {
        Task task;
        {
            // 先获取锁
            std::unique_lock<std::mutex> lock(_taskQueMtx);
            // std::cout << "tid: " << std::this_thread::get_id() << "
            // 尝试获取任务..." << std::endl;

            // cache模式下，有可能已经创建了很多的线程，但是空闲时间超过60s，应该把多余的线程给取消了
            // 当前时间 - 上一次线程执行时间 > 60s
            // 等待nonEmpty
            // 总结：也就是线程获取任务的过程中等待过长时间就可以进行删除

            // 每1s返回一次，区分 超时返回？有任务待执行
            while (_taskSize <= 0) {
                // 条件变量，超时返回
                if (_poolMode == ThreadPoolMode::ThreadPoolModeCACHED) {
                    if (std::cv_status::timeout ==
                        _nonEmpty.wait_for(lock, std::chrono::seconds(1))) {
                        auto now = std::chrono::high_resolution_clock().now();
                        auto dur = std::chrono::duration_cast<std::chrono::seconds>(
                                now - lastTime);
                        if (dur.count() >= THREAD_MAX_IDLE_TIME &&
                            _curThreadSize > _coreThreadSize) {
                            // 开始回收当前线程
                            // 记录线程数量的相关变量的值修改
                            // 把线程对象从线程列表容器中删除
                            // 到那时没有办法直到对应的thread的线程对象 threadid => thread对象
                            _threads.erase(threadId); // 删除id，不能用this_thread里面的id
                            _curThreadSize--;
                            _idleThreadSize--;
                            //LogD("threadid exit!");
                            return;
                        }
                    }
                } else {
                    _nonEmpty.wait(lock);
                }
                // 线程等待被唤醒后，两种情况都检查是被谁唤醒
                // 线程池要结束，回收线程资源
                if (!_isPoolRunning) {
                    // erase就是调用了unique_ptr的析构函数，会把Thread删除，这里就已经删除线程了
                    _threads.erase(threadId); // 删除id，不能用this_thread里面的id
                    //LogD("threadid exit!");
                    _exitCond.notify_all();
                    return;
                }
            }
            _taskSize--;
            // 取任务
            // 是不是这里取任务有问题,加了打印就正常,概率性的,没有锁在加减任务的时候就会有问题
            // std::cout << "tid: " << std::this_thread::get_id() << " 获取任务成功"
            // << std::endl;
            task = _taskQue.front();
            _taskQue.pop();

            // 如果依然有剩余的任务，继续通知其他线程执行任务
            if (_taskQue.size() > 0) { // 第一个吃螃蟹的通知其他人吃螃蟹
                _nonEmpty.notify_all();
            }
            // 取出一个任务，进行通知，通知可以继续提交生产任务
            _notFull.notify_all();
            _idleThreadSize--;
        }

        // 释放锁,执行任务
        if (task != nullptr) {
            // task->run();
            // 执行任务：把任务的返回值通过setVal方法给到Result
            // task->exec();
            task(); // 执行package_task封装的函数，执行完会post
        }
        {
            std::unique_lock<std::mutex> lock(_taskQueMtx);
            // 返回值处理
            _idleThreadSize++; // 线程执行完了任务，空闲线程++
        }

        // 更新线程调度执行完任务的时间
        lastTime = std::chrono::high_resolution_clock().now();
    }
    // isRunLooping_表示部分线程在线程池要退出的时候刚好在执行任务，执行后到这里
    _threads.erase(threadId); // 删除id，不能用this_thread里面的id
    //LogD("threadid exit!");
    _exitCond.notify_all();
}

bool ThreadPool::getPoolState() { return _isPoolRunning; }

ThreadPoolMode ThreadPool::getThreadPoolMode() const { return _poolMode; }

void ThreadPool::setThreadPoolMode(ThreadPoolMode mode) {
    if (!_isPoolRunning) {
        _poolMode = mode;
    }
}

void ThreadPool::setThreadMaxThreshHold(int threshHold) {
    if (!_isPoolRunning && _poolMode == ThreadPoolMode::ThreadPoolModeCACHED &&
            threshHold > _coreThreadSize) {
        _threadMaxThreshHold = threshHold;
    }
}

int ThreadPool::getThreadMaxThreshHold() const { return _threadMaxThreshHold; }

void ThreadPool::setTaskQueMaxThreshHold(int threshHold) {
    _taskQueMaxThreshHold = threshHold;
}

int ThreadPool::getTaskQueMaxThreshHold() const {
    return _taskQueMaxThreshHold;
}

int ThreadPool::getCoreThreadSize() const { return _coreThreadSize; }

int ThreadPool::getCurThreadSize() const { return _curThreadSize; }

int ThreadPool::getIdleThreadSize() const { return _idleThreadSize; }

int ThreadPool::getTaskSize() const { return _taskSize; }

bool ThreadPool::taskIsFull() const {
    return _taskQue.size() >= _taskQueMaxThreshHold;
}